// Copyright (c) 2002 Graz University of Technology. All rights reserved.
// 
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
// 
// 1. Redistributions of source code must retain the above copyright notice, this
//    list of conditions and the following disclaimer.
// 
// 2. Redistributions in binary form must reproduce the above copyright notice,
//    this list of conditions and the following disclaimer in the documentation
//    and/or other materials provided with the distribution.
// 
// 3. The end-user documentation included with the redistribution, if any, must
//    include the following acknowledgment:
// 
//    "This product includes software developed by IAIK of Graz University of
//     Technology."
// 
//    Alternately, this acknowledgment may appear in the software itself, if and
//    wherever such third-party acknowledgments normally appear.
// 
// 4. The names "Graz University of Technology" and "IAIK of Graz University of
//    Technology" must not be used to endorse or promote products derived from this
//    software without prior written permission.
// 
// 5. Products derived from this software may not be called "IAIK PKCS Wrapper",
//    nor may "IAIK" appear in their name, without prior written permission of
//    Graz University of Technology.
// 
// THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE LICENSOR BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
// OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

package iaik.pkcs.pkcs11;

import iaik.pkcs.pkcs11.wrapper.CK_MECHANISM_INFO;
import iaik.pkcs.pkcs11.wrapper.Constants;
import iaik.pkcs.pkcs11.wrapper.PKCS11Constants;

/**
 * Objects of this class provide information about a certain mechanism that a
 * token implements.
 *
 * @author <a href="mailto:Karl.Scheibelhofer@iaik.at"> Karl Scheibelhofer </a>
 * @version 1.0
 * @invariants
 */
public class MechanismInfo implements Cloneable {

	/**
	 * The minimum key length supported by this algorithm.
	 */
	protected long minKeySize_;

	/**
	 * The maximum key length supported by this algorithm.
	 */
	protected long maxKeySize_;

	/**
	 * Contains all feature flags of this mechanism info.
	 */
	protected long flags_;

	/**
	 * Default constructor. All memeber variables get the defualt value for their 
	 * tpye.
	 *
	 * @preconditions
	 * @postconditions
	 */
	public MechanismInfo() { /* left empty intentionally */
	}

	/**
	 * Constructor taking a CK_MECHANISM_INFO object as data source.
	 *
	 * @param ckMechanismInfo The CK_MECHANISM_INFO object that provides the data.
	 * @preconditions (ckMechanismInfo <> null)
	 * @postconditions
	 */
	public MechanismInfo(CK_MECHANISM_INFO ckMechanismInfo) {
		if (ckMechanismInfo == null) {
			throw new NullPointerException("Argument \"ckMechanismInfo\" must not be null.");
		}
		minKeySize_ = ckMechanismInfo.ulMinKeySize;
		maxKeySize_ = ckMechanismInfo.ulMaxKeySize;
		flags_ = ckMechanismInfo.flags;
	}

	/**
	 * Create a (deep) clone of this object.
	 *
	 * @return A clone of this object.
	 * @preconditions
	 * @postconditions (result <> null)
	 *                 and (result instanceof MechanismInfo)
	 *                 and (result.equals(this))
	 */
	public java.lang.Object clone() {
		MechanismInfo clone;

		try {
			clone = (MechanismInfo) super.clone();
		} catch (CloneNotSupportedException ex) {
			// this must not happen, because this class is cloneable
			throw new TokenRuntimeException("An unexpected clone exception occurred.", ex);
		}

		return clone;
	}

	/**
	 * Override equals to check for the equality of mechanism infos.
	 *
	 * @param otherObject The other MechanismInfo object.
	 * @return True, if other is an instance of this class and
	 *         all member variables are equal.
	 * @preconditions
	 * @postconditions
	 */
	public boolean equals(Object otherObject) {
		boolean euqal = false;

		if (otherObject instanceof MechanismInfo) {
			MechanismInfo other = (MechanismInfo) otherObject;
			euqal = (this == other)
			    || ((this.minKeySize_ == other.minKeySize_)
			        && (this.maxKeySize_ == other.maxKeySize_) && (this.flags_ == other.flags_));
		}

		return euqal;
	}

	/**
	 * Override hashCode to ensure that hashtable still works after overriding
	 * equals.
	 *
	 * @return The hash code of this object. Taken from the mechanism code.
	 * @preconditions
	 * @postconditions
	 */
	public int hashCode() {
		return (int) (minKeySize_ ^ maxKeySize_ ^ flags_);
	}

	/**
	 * Get the minimum key length supported by this mechanism.
	 *
	 * @return The minimum key length supported by this mechanism..
	 * @preconditions
	 * @postconditions
	 */
	public long getMinKeySize() {
		return minKeySize_;
	}

	/**
	 * Get the maximum key length supported by this mechanism.
	 *
	 * @return The maximum key length supported by this mechanism.
	 * @preconditions
	 * @postconditions
	 */
	public long getMaxKeySize() {
		return maxKeySize_;
	}

	/**
	 * Check, if this mechanism is performed in hardware.
	 *
	 * @return True, if this mechanism is performed in hardware.
	 * @preconditions
	 * @postconditions
	 */
	public boolean isHw() {
		return (flags_ & PKCS11Constants.CKF_HW) != 0L;
	}

	/**
	 * Check, if this mechanism can be used for encryption.
	 *
	 * @return True, if this mechanism can be used for encrpytion.
	 * @preconditions
	 * @postconditions
	 */
	public boolean isEncrypt() {
		return (flags_ & PKCS11Constants.CKF_ENCRYPT) != 0L;
	}

	/**
	 * Check, if this mechanism can be used for decryption.
	 *
	 * @return True, if this mechanism can be used for decrpytion.
	 * @preconditions
	 * @postconditions
	 */
	public boolean isDecrypt() {
		return (flags_ & PKCS11Constants.CKF_DECRYPT) != 0L;
	}

	/**
	 * Check, if this mechanism can be used for digesting.
	 *
	 * @return True, if this mechanism can be used for digesting.
	 * @preconditions
	 * @postconditions
	 */
	public boolean isDigest() {
		return (flags_ & PKCS11Constants.CKF_DIGEST) != 0L;
	}

	/**
	 * Check, if this mechanism can be used for signing.
	 *
	 * @return True, if this mechanism can be used for signing.
	 * @preconditions
	 * @postconditions
	 */
	public boolean isSign() {
		return (flags_ & PKCS11Constants.CKF_SIGN) != 0L;
	}

	/**
	 * Check, if this mechanism can be used for signing with data recovery.
	 *
	 * @return True, if this mechanism can be used for signing with data recovery.
	 * @preconditions
	 * @postconditions
	 */
	public boolean isSignRecover() {
		return (flags_ & PKCS11Constants.CKF_SIGN_RECOVER) != 0L;
	}

	/**
	 * Check, if this mechanism can be used for verification.
	 *
	 * @return True, if this mechanism can be used for verification.
	 * @preconditions
	 * @postconditions
	 */
	public boolean isVerify() {
		return (flags_ & PKCS11Constants.CKF_VERIFY) != 0L;
	}

	/**
	 * Check, if this mechanism can be used for verification with data recovery.
	 *
	 * @return True, if this mechanism can be used for verification with data recovery.
	 * @preconditions
	 * @postconditions
	 */
	public boolean isVerifyRecover() {
		return (flags_ & PKCS11Constants.CKF_VERIFY_RECOVER) != 0L;
	}

	/**
	 * Check, if this mechanism can be used for secret key generation.
	 *
	 * @return True, if this mechanism can be used for secret key generation.
	 * @preconditions
	 * @postconditions
	 */
	public boolean isGenerate() {
		return (flags_ & PKCS11Constants.CKF_GENERATE) != 0L;
	}

	/**
	 * Check, if this mechanism can be used for key-pair generation.
	 *
	 * @return True, if this mechanism can be used for key-pair generation.
	 * @preconditions
	 * @postconditions
	 */
	public boolean isGenerateKeyPair() {
		return (flags_ & PKCS11Constants.CKF_GENERATE_KEY_PAIR) != 0L;
	}

	/**
	 * Check, if this mechanism can be used for key wrapping.
	 *
	 * @return True, if this mechanism can be used for key wrapping.
	 * @preconditions
	 * @postconditions
	 */
	public boolean isWrap() {
		return (flags_ & PKCS11Constants.CKF_WRAP) != 0L;
	}

	/**
	 * Check, if this mechanism can be used for key unwrapping.
	 *
	 * @return True, if this mechanism can be used for key unwrapping.
	 * @preconditions
	 * @postconditions
	 */
	public boolean isUnwrap() {
		return (flags_ & PKCS11Constants.CKF_UNWRAP) != 0L;
	}

	/**
	 * Check, if this mechanism can be used for key derivation.
	 *
	 * @return True, if this mechanism can be used for key derivation.
	 * @preconditions
	 * @postconditions
	 */
	public boolean isDerive() {
		return (flags_ & PKCS11Constants.CKF_DERIVE) != 0L;
	}

	/**
	 * Check, if this mechanism can be used with EC domain parameters over Fp.
	 *
	 * @return True, if this mechanism can be used with EC domain parameters over
	 *         Fp.
	 * @preconditions
	 * @postconditions
	 */
	public boolean isEcFp() {
		return (flags_ & PKCS11Constants.CKF_EC_F_P) != 0L;
	}

	/**
	 * Check, if this mechanism can be used with EC domain parameters over F2m.
	 *
	 * @return True, if this mechanism can be used with EC domain parameters over
	 *         F2m.
	 * @preconditions
	 * @postconditions
	 */
	public boolean isEcF2m() {
		return (flags_ & PKCS11Constants.CKF_EC_F_2M) != 0L;
	}

	/**
	 * Check, if this mechanism can be used with EC domain parameters of the
	 * choice ecParameters.
	 *
	 * @return True, if this mechanism can be used with EC domain parameters of
	 *         the choice ecParameters.
	 * @preconditions
	 * @postconditions
	 */
	public boolean isEcEcParameters() {
		return (flags_ & PKCS11Constants.CKF_EC_ECPARAMETERS) != 0L;
	}

	/**
	 * Check, if this mechanism can be used with EC domain parameters of the
	 * choice namedCurve.
	 *
	 * @return True, if this mechanism can be used with EC domain parameters of
	 *         the choice namedCurve.
	 * @preconditions
	 * @postconditions
	 */
	public boolean isEcNamedCurve() {
		return (flags_ & PKCS11Constants.CKF_EC_NAMEDCURVE) != 0L;
	}

	/**
	 * Check, if this mechanism can be used with elliptic curve point
	 * uncompressed.
	 *
	 * @return True, if this mechanism can be used with elliptic curve point
	 *         uncompressed.
	 * @preconditions
	 * @postconditions
	 */
	public boolean isEcUncompress() {
		return (flags_ & PKCS11Constants.CKF_EC_UNCOMPRESS) != 0L;
	}

	/**
	 * Check, if this mechanism can be used with elliptic curve point compressed.
	 *
	 * @return True, if this mechanism can be used with elliptic curve point
	 *         compressed.
	 * @preconditions
	 * @postconditions
	 */
	public boolean isEcCompress() {
		return (flags_ & PKCS11Constants.CKF_EC_COMPRESS) != 0L;
	}

	/**
	 * Check, if there is an extension to the flags; false, if no extensions.
	 * Must be false for this version of PKCS#11.
	 *
	 * @return False for this version.
	 * @preconditions
	 * @postconditions
	 */
	public boolean isExtension() {
		return (flags_ & PKCS11Constants.CKF_EXTENSION) != 0L;
	}

	/**
	 * Create a new MechanismInfo objects whichs flags are a logical OR of this
	 * object's flags and the other object's flags. The minimum key size is set
	 * to the lower of both key sizes and the maximum key size is set to the
	 * higher of both key sizes.
	 * If the other is null, the new object has the same contents as this object.
	 *
	 * @param other The other MechanismInfo object.
	 * @return A new MechanismInfo that is a logical OR of this and other.
	 * @preconditions
	 * @postconditions
	 */
	public MechanismInfo or(MechanismInfo other) {
		MechanismInfo result;

		if (other != null) {
			result = new MechanismInfo();
			result.flags_ = this.flags_ | other.flags_;
			result.minKeySize_ = (this.minKeySize_ < other.minKeySize_) ? this.minKeySize_
			    : other.minKeySize_;
			result.maxKeySize_ = (this.maxKeySize_ > other.maxKeySize_) ? this.maxKeySize_
			    : other.maxKeySize_;
		} else {
			result = (MechanismInfo) this.clone();
		}

		return result;
	}

	/**
	 * Create a new MechanismInfo objects whichs flags are a logical AND of this
	 * object's flags and the other object's flags. The minimum key size is set
	 * to the higher of both key sizes and the maximum key size is set to the
	 * lower of both key sizes.
	 * If the other is null, the new object has no flags set and its key sizes
	 * set to zero.
	 *
	 * @param other The other MechanismInfo object.
	 * @return A new MechanismInfo that is a logical AND of this and other.
	 * @preconditions
	 * @postconditions
	 */
	public MechanismInfo and(MechanismInfo other) {
		MechanismInfo result = new MechanismInfo();

		if (other != null) {
			result.flags_ = this.flags_ & other.flags_;
			result.minKeySize_ = (this.minKeySize_ > other.minKeySize_) ? this.minKeySize_
			    : other.minKeySize_;
			result.maxKeySize_ = (this.maxKeySize_ < other.maxKeySize_) ? this.maxKeySize_
			    : other.maxKeySize_;
		}

		return result;
	}

	/**
	 * Create a new MechanismInfo objects whichs flags are a logical NOT of this
	 * object's flags. The key sizes remain the same.
	 *
	 * @return A new MechanismInfo that is a logical NOT of this object.
	 * @preconditions
	 * @postconditions
	 */
	public MechanismInfo not() {
		MechanismInfo result = (MechanismInfo) this.clone();

		result.flags_ = ~this.flags_;

		return result;
	}

	/**
	 * Set the minimum key length supported by this mechanism.
	 *
	 * @param minKeySize The minimum key length supported by this mechanism.
	 * @preconditions
	 * @postconditions
	 */
	public void setMinKeySize(long minKeySize) {
		minKeySize_ = minKeySize;
	}

	/**
	/**
	 * Set the maximum key length supported by this mechanism.
	 *
	 * @param maxKeySize The maximum key length supported by this mechanism.
	 * @preconditions
	 * @postconditions
	 */
	public void setMaxKeySize(long maxKeySize) {
		maxKeySize_ = maxKeySize;
	}

	/**
	 * Set, if this mechanism is performed in hardware.
	 *
	 * @param hw True, if this mechanism is performed in hardware.
	 * @preconditions
	 * @postconditions
	 */
	public void setHw(boolean hw) {
		setFlagBit(PKCS11Constants.CKF_HW, hw);
	}

	/**
	 * Set if this mechanism can be used for encryption.
	 *
	 * @param encrypt True, if this mechanism can be used for encrpytion.
	 * @preconditions
	 * @postconditions
	 */
	public void setEncrypt(boolean encrypt) {
		setFlagBit(PKCS11Constants.CKF_ENCRYPT, encrypt);
	}

	/**
	 * Set if this mechanism can be used for decryption.
	 *
	 * @param decrypt True, if this mechanism can be used for decrpytion.
	 * @preconditions
	 * @postconditions
	 */
	public void setDecrypt(boolean decrypt) {
		setFlagBit(PKCS11Constants.CKF_DECRYPT, decrypt);
	}

	/**
	 * Set if this mechanism can be used for digesting.
	 *
	 * @param digest True, if this mechanism can be used for digesting.
	 * @preconditions
	 * @postconditions
	 */
	public void setDigest(boolean digest) {
		setFlagBit(PKCS11Constants.CKF_DIGEST, digest);
	}

	/**
	 * Set if this mechanism can be used for signing.
	 *
	 * @param sign True, if this mechanism can be used for signing.
	 * @preconditions
	 * @postconditions
	 */
	public void setSign(boolean sign) {
		setFlagBit(PKCS11Constants.CKF_SIGN, sign);
	}

	/**
	 * Set if this mechanism can be used for signing with data recovery.
	 *
	 * @param signRecover True, if this mechanism can be used for signing with data recovery.
	 * @preconditions
	 * @postconditions
	 */
	public void setSignRecover(boolean signRecover) {
		setFlagBit(PKCS11Constants.CKF_SIGN_RECOVER, signRecover);
	}

	/**
	 * Set if this mechanism can be used for verification.
	 *
	 * @param verfy True, if this mechanism can be used for verification.
	 * @preconditions
	 * @postconditions
	 */
	public void setVerify(boolean verfy) {
		setFlagBit(PKCS11Constants.CKF_VERIFY, verfy);
	}

	/**
	 * Set if this mechanism can be used for verification with data recovery.
	 *
	 * @param verifyRecover True, if this mechanism can be used for verification with data recovery.
	 * @preconditions
	 * @postconditions
	 */
	public void setVerifyRecover(boolean verifyRecover) {
		setFlagBit(PKCS11Constants.CKF_VERIFY_RECOVER, verifyRecover);
	}

	/**
	 * Set if this mechanism can be used for secret key generation.
	 *
	 * @param generate True, if this mechanism can be used for secret key generation.
	 * @preconditions
	 * @postconditions
	 */
	public void setGenerate(boolean generate) {
		setFlagBit(PKCS11Constants.CKF_GENERATE, generate);
	}

	/**
	 * Set if this mechanism can be used for key-pair generation.
	 *
	 * @param generateKeyPair True, if this mechanism can be used for key-pair generation.
	 * @preconditions
	 * @postconditions
	 */
	public void setGenerateKeyPair(boolean generateKeyPair) {
		setFlagBit(PKCS11Constants.CKF_GENERATE_KEY_PAIR, generateKeyPair);
	}

	/**
	 * Set if this mechanism can be used for key wrapping.
	 *
	 * @param wrap True, if this mechanism can be used for key wrapping.
	 * @preconditions
	 * @postconditions
	 */
	public void setWrap(boolean wrap) {
		setFlagBit(PKCS11Constants.CKF_WRAP, wrap);
	}

	/**
	 * Set if this mechanism can be used for key unwrapping.
	 *
	 * @param unwrap True, if this mechanism can be used for key unwrapping.
	 * @preconditions
	 * @postconditions
	 */
	public void setUnwrap(boolean unwrap) {
		setFlagBit(PKCS11Constants.CKF_UNWRAP, unwrap);
	}

	/**
	 * Set if this mechanism can be used for key derivation.
	 *
	 * @param derive True, if this mechanism can be used for key derivation.
	 * @preconditions
	 * @postconditions
	 */
	public void setDerive(boolean derive) {
		setFlagBit(PKCS11Constants.CKF_DERIVE, derive);
	}

	/**
	 * Set if this mechanism can be used with EC domain parameters over Fp.
	 *
	 * @param ecFp True, if this mechanism can be used with EC domain parameters
	 *             over Fp.
	 * @preconditions
	 * @postconditions
	 */
	public void setEcFp(boolean ecFp) {
		setFlagBit(PKCS11Constants.CKF_EC_F_P, ecFp);
	}

	/**
	 * Set if this mechanism can be used with EC domain parameters over F2m.
	 *
	 * @param ecF2m True, if this mechanism can be used with EC domain parameters
	 *              over F2m.
	 * @preconditions
	 * @postconditions
	 */
	public void setEcF2m(boolean ecF2m) {
		setFlagBit(PKCS11Constants.CKF_EC_F_2M, ecF2m);
	}

	/**
	 * Set if this mechanism can be used with EC domain parameters of the
	 * choice ecParameters.
	 *
	 * @param ecEcParameters True, if this mechanism can be used with EC domain
	 *                       parameters of the choice ecParameters.
	 * @preconditions
	 * @postconditions
	 */
	public void setEcEcParameters(boolean ecEcParameters) {
		setFlagBit(PKCS11Constants.CKF_EC_ECPARAMETERS, ecEcParameters);
	}

	/**
	 * Set if this mechanism can be used with EC domain parameters of the
	 * choice namedCurve.
	 *
	 * @param ecNamedCurve True, if this mechanism can be used with EC domain
	 *                     parameters of the choice namedCurve.
	 * @preconditions
	 * @postconditions
	 */
	public void setEcNamedCurve(boolean ecNamedCurve) {
		setFlagBit(PKCS11Constants.CKF_EC_NAMEDCURVE, ecNamedCurve);
	}

	/**
	 * Set if this mechanism can be used with elliptic curve point
	 * uncompressed.
	 *
	 * @param ecUncompress True, if this mechanism can be used with elliptic
	 *                     curve point uncompressed.
	 * @preconditions
	 * @postconditions
	 */
	public void setEcUncompress(boolean ecUncompress) {
		setFlagBit(PKCS11Constants.CKF_EC_UNCOMPRESS, ecUncompress);
	}

	/**
	 * Set if this mechanism can be used with elliptic curve point compressed.
	 *
	 * @param ecCompress True, if this mechanism can be used with elliptic curve
	 *                   point compressed.
	 * @preconditions
	 * @postconditions
	 */
	public void setEcCompress(boolean ecCompress) {
		setFlagBit(PKCS11Constants.CKF_EC_COMPRESS, ecCompress);
	}

	/**
	 * Set if there is an extension to the flags; false, if no extensions.
	 * Must be false for this version.
	 *
	 * @param extension False for this version.
	 * @preconditions
	 * @postconditions
	 */
	public void setExtension(boolean extension) {
		setFlagBit(PKCS11Constants.CKF_EXTENSION, extension);
	}

	/**
	 * Check, if this mechanism info has those flags set to true, which are set
	 * in the given mechanism info. This may be used as a simple check, if some
	 * operations are supported.
	 * This also checks the key length range, if they are specified in the given
	 * mechaism object; i.e. if they are not zero.
	 *
	 * @param requiredFeatures The required features.
	 * @return True, if the required features are supported.
	 * @preconditions (requiredFeatures <> null)
	 * @postconditions
	 */
	public boolean supports(MechanismInfo requiredFeatures) {
		if (requiredFeatures == null) {
			throw new NullPointerException("Argument \"requiredFeatures\" must not be null.");
		}

		long requiredMaxKeySize = requiredFeatures.getMaxKeySize();
		if ((requiredMaxKeySize != 0) && (requiredMaxKeySize > maxKeySize_)) {
			return false;
		}

		long requiredMinKeySize = requiredFeatures.getMinKeySize();
		if ((requiredMinKeySize != 0) && (requiredMinKeySize < minKeySize_)) {
			return false;
		}

		if ((requiredFeatures.flags_ & flags_) != requiredFeatures.flags_) {
			return false;
		}

		return true;
	}

	/**
	 * Returns the string representation of this object.
	 *
	 * @return the string representation of this object
	 */
	public String toString() {
		StringBuffer buffer = new StringBuffer(256);

		buffer.append(Constants.INDENT);
		buffer.append("Minimum Key-Size: ");
		buffer.append(minKeySize_);

		buffer.append(Constants.NEWLINE);
		buffer.append(Constants.INDENT);
		buffer.append("Maximum Key-Size: ");
		buffer.append(maxKeySize_);

		buffer.append(Constants.NEWLINE);
		buffer.append(Constants.INDENT);
		buffer.append("Hardware: ");
		buffer.append(isHw());

		buffer.append(Constants.NEWLINE);
		buffer.append(Constants.INDENT);
		buffer.append("Encrypt: ");
		buffer.append(isEncrypt());

		buffer.append(Constants.NEWLINE);
		buffer.append(Constants.INDENT);
		buffer.append("Decrypt: ");
		buffer.append(isDecrypt());

		buffer.append(Constants.NEWLINE);
		buffer.append(Constants.INDENT);
		buffer.append("Digest: ");
		buffer.append(isDigest());

		buffer.append(Constants.NEWLINE);
		buffer.append(Constants.INDENT);
		buffer.append("Sign: ");
		buffer.append(isSign());

		buffer.append(Constants.NEWLINE);
		buffer.append(Constants.INDENT);
		buffer.append("Sign Recover: ");
		buffer.append(isSignRecover());

		buffer.append(Constants.NEWLINE);
		buffer.append(Constants.INDENT);
		buffer.append("Verify: ");
		buffer.append(isVerify());

		buffer.append(Constants.NEWLINE);
		buffer.append(Constants.INDENT);
		buffer.append("Verify Recover: ");
		buffer.append(isVerifyRecover());

		buffer.append(Constants.NEWLINE);
		buffer.append(Constants.INDENT);
		buffer.append("Generate: ");
		buffer.append(isGenerate());

		buffer.append(Constants.NEWLINE);
		buffer.append(Constants.INDENT);
		buffer.append("Generate Key-Pair: ");
		buffer.append(isGenerateKeyPair());

		buffer.append(Constants.NEWLINE);
		buffer.append(Constants.INDENT);
		buffer.append("Wrap: ");
		buffer.append(isWrap());

		buffer.append(Constants.NEWLINE);
		buffer.append(Constants.INDENT);
		buffer.append("Unwrap: ");
		buffer.append(isUnwrap());

		buffer.append(Constants.NEWLINE);
		buffer.append(Constants.INDENT);
		buffer.append("Derive: ");
		buffer.append(isDerive());

		buffer.append(Constants.NEWLINE);
		buffer.append(Constants.INDENT);
		buffer.append("EC F(p): ");
		buffer.append(isEcFp());

		buffer.append(Constants.NEWLINE);
		buffer.append(Constants.INDENT);
		buffer.append("EC F(2^m): ");
		buffer.append(isEcF2m());

		buffer.append(Constants.NEWLINE);
		buffer.append(Constants.INDENT);
		buffer.append("EC Parameters: ");
		buffer.append(isEcEcParameters());

		buffer.append(Constants.NEWLINE);
		buffer.append(Constants.INDENT);
		buffer.append("EC Named Curve: ");
		buffer.append(isEcNamedCurve());

		buffer.append(Constants.NEWLINE);
		buffer.append(Constants.INDENT);
		buffer.append("EC Uncompress: ");
		buffer.append(isEcUncompress());

		buffer.append(Constants.NEWLINE);
		buffer.append(Constants.INDENT);
		buffer.append("EC Compress: ");
		buffer.append(isEcCompress());

		buffer.append(Constants.NEWLINE);
		buffer.append(Constants.INDENT);
		buffer.append("Extension: ");
		buffer.append(isExtension());

		return buffer.toString();
	}

	/**
	 * Set the given feature flag(s) to the given value.
	 *
	 * @param flagMask The mask of the flag bit(s).
	 * @param value True to set the flag(s), false to clear the flag(s).
	 * @preconditions
	 * @postconditions
	 */
	protected void setFlagBit(long flagMask, boolean value) {
		if (value) {
			flags_ |= flagMask;
		} else {
			flags_ &= ~flagMask;
		}
	}

}
